package org.hbgl.geoentity.admin.shiro;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.hbgl.geoentity.core.util.bcrypt.BCryptPasswordEncoder;
import org.hbgl.geoentity.db.domain.GeUser;
import org.hbgl.geoentity.db.service.GePermissionService;
import org.hbgl.geoentity.db.service.GeRoleService;
import org.hbgl.geoentity.db.service.GeUserService;
import org.hbgl.geoentity.db.util.EnumFile;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;

/**
 * 下面三个Autowired注解需要配合Lazy注解使用，否则会导致这三个service相关的事务失效。
 * https://gitee.com/hbgl/litemall/issues/I3I94X#note_4809495
 */
@Component
public class AdminRealm extends AuthorizingRealm {
    @Resource
    @Lazy
    private GeUserService userService;
    @Resource
    @Lazy
    private GeRoleService roleService;
    @Resource
    @Lazy
    private GePermissionService permissionService;

    @Override
    public String getName() {
        return UserType.ADMIN;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }

        LoginUser loginUser = (LoginUser) getAvailablePrincipal(principals);
        GeUser user = loginUser.getAdminUser();
        if (user != null) {
            Integer roleId = user.getRoleId();
            Set<String> roles = roleService.queryByIds(new Integer[]{roleId});
            Set<String> permissions = permissionService.queryByRoleIds(new Integer[]{roleId});
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.setRoles(roles);
            info.setStringPermissions(permissions);
            return info;
        }
        return new SimpleAuthorizationInfo();
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (!(authenticationToken instanceof CustomLoginToken)) {
            throw new UnknownAccountException("联系管理员，不是CustomLoginToken的对象");
        }
        Object userObj = SecurityUtils.getSubject().getPrincipal();
        LoginUser loginUser;
        if (userObj == null) {
            loginUser = new LoginUser();
        } else {
            loginUser = (LoginUser) userObj;
        }
        CustomLoginToken token = (CustomLoginToken) authenticationToken;
        String username = token.getUsername();
        String password = new String(token.getPassword());

        if (StringUtils.isBlank(username)) {
            throw new AccountException("用户名不能为空");
        }
        if (StringUtils.isBlank(password)) {
            throw new AccountException("密码不能为空");
        }

        List<GeUser> userList = userService.findByUsernameCheck(username, true);
        Assert.state(userList.size() < 2, "同一个用户名存在两个账户");
        if (userList.size() == 0) {
            throw new UnknownAccountException("找不到用户（" + username + "）的帐号信息");
        }
        GeUser user = userList.get(0);
        if (user.getUnitId() != 1) {
            throw new UnknownAccountException("找不到用户（" + username + "）的帐号信息");
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(password, user.getPassword())) {
            throw new UnknownAccountException("找不到用户（" + username + "）的帐号信息");
        }
        if (user.getState() == EnumFile.CommonState.Disable) {
            throw new LockedAccountException("用户（" + username + "）的帐号已被禁用");
        }
        loginUser.setAdminUser(user);
        return new SimpleAuthenticationInfo(loginUser, password, getName());
    }
}
